//go:build wireinject
// +build wireinject

package di

import (
	"context"
	"sync"

	"gitee.com/lyuanbo/stock-plus/internal/models"
	"gitee.com/lyuanbo/stock-plus/pkg/asynqx"
	"gitee.com/lyuanbo/stock-plus/pkg/db/clickhousex"
	"gitee.com/lyuanbo/stock-plus/pkg/logger"
	"gitee.com/lyuanbo/stock-plus/pkg/mq/natsx"
	"gitee.com/lyuanbo/stock-plus/pkg/stock"
	"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
	"github.com/go-redsync/redsync/v4"
	"github.com/google/wire"
	"github.com/nats-io/nats.go"
	"go.uber.org/zap/zapcore"

	"gitee.com/lyuanbo/stock-plus/internal/config"
	"gitee.com/lyuanbo/stock-plus/internal/svc"
	"gitee.com/lyuanbo/stock-plus/pkg/cache/redisx"
	"github.com/redis/go-redis/v9"
)

func ProvideContext() context.Context {
	return context.Background()
}

func ProvideMutex() *sync.Mutex {
	return &sync.Mutex{}
}

func ProvideToken(cfg *config.Conf) string {
	return cfg.XueQiu.Token
}

func ProvideLogger(cfg *config.Conf) *logger.Logger {
	level, _ := zapcore.ParseLevel(cfg.Log.Level)
	log, err := logger.NewLogger(
		cfg.Log.Filename,
		cfg.Log.MaxSize,
		cfg.Log.MaxBackups,
		cfg.Log.MaxAge,
		cfg.Log.Compress,
		level,
	)
	if err != nil {
		panic(err)
	}
	return log
}

func ProvideNats(ctx context.Context, cfg *config.Conf, log *logger.Logger) *nats.Conn {
	return natsx.New(ctx, cfg.Nats.URL, cfg.Nats.User, cfg.Nats.Password, log)
}

func ProvideAsynq(ctx context.Context, cfg *config.Conf, log *logger.Logger) *asynqx.Worker {
	return asynqx.New(ctx, cfg.Redis.Addr, cfg.Redis.Password, log)
}

func ProvideRedis(ctx context.Context, cfg *config.Conf, log *logger.Logger) *redis.Client {
	return redisx.New(cfg.Redis.Addr, cfg.Redis.Password, cfg.Redis.DB)
}

func ProvideConfig(cfg *config.DynamicConf) *config.Conf {
	return cfg.Get()
}

func ProvideClickHouse(ctx context.Context, cfg *config.Conf) driver.Conn {
	return clickhousex.New(ctx, cfg.ClickHouse.Addr, cfg.ClickHouse.Database, cfg.ClickHouse.Username, cfg.ClickHouse.Password)
}

var InfraProviderSet = wire.NewSet(
	ProvideContext,
	config.New,
	ProvideConfig,
	ProvideLogger,
	ProvideRedis,
	redisx.NewRedisSync,
	ProvideNats,
	ProvideMutex,
	ProvideToken,
	stock.New,
	ProvideClickHouse,
	ProviderModels,
)

func ProviderOptions(ctx context.Context, mux *sync.Mutex, conf *config.DynamicConf, rds *redis.Client,
	redsync *redsync.Redsync, nats *nats.Conn, stock *stock.Stock, clickhouse driver.Conn, logger *logger.Logger, models svc.Models) svc.Options {
	return svc.Options{
		Ctx:        ctx,
		Conf:       conf,
		Mux:        mux,
		Redis:      rds,
		RedSync:    redsync,
		Nats:       nats,
		Stock:      stock,
		Logger:     logger,
		ClickHouse: clickhouse,
		Models:     models,
	}
}

func ProviderModels(ctx context.Context, clickhouse driver.Conn) svc.Models {
	return svc.Models{
		Realtime: models.NewRealtimeModel(ctx, clickhouse),
		Kline:    models.NewKlineModel(ctx, clickhouse),
	}
}

var ServiceProviderSet = wire.NewSet(
	InfraProviderSet,
	ProviderOptions,
	svc.NewServiceContext,
)

var BusinessProviderSet = wire.NewSet(
	ServiceProviderSet,
	svc.NewStockService,
	svc.NewHTTPService,
)

type App struct {
	SvcCtx   *svc.ServiceContext
	StockSvc *svc.StockSvc
	HTTPSvc  *svc.HTTPSvc
}

func NewApp(svcCtx *svc.ServiceContext, stockSvc *svc.StockSvc, httpSvc *svc.HTTPSvc) *App {
	return &App{
		SvcCtx:   svcCtx,
		StockSvc: stockSvc,
		HTTPSvc:  httpSvc,
	}
}

func Init() *App {
	wire.Build(BusinessProviderSet, NewApp)
	return nil
}
